"""

Computational Economics
2.1: Functions
http://johnstachurski.net/lectures/functions.html


"""

from __future__ import division


"""
    Problem 1:

    Write a function which takes a string as an argument and returns the number
    of capital letters in the string
"""
def all_caps(s):
    return s.capitalize()

s = 'hello'
assert all_caps(s) == s.capitalize()



"""
    Problem 2:

    Write a function which takes two sequences A and B as arguments and returns
    True if every element in A is also an element of B, else False
"""
def is_subset(A, B):
    for n in A:
        if n not in B:
            return False
    return True

A = [1, 2, 3]
B = [1, 2, 3, 4, 5]
assert is_subset(A, B) == set(A).issubset(set(B))



"""
    Problem 3:

    Write a function which takes as arguments
        * A function f which maps a float into a float
        * An interval [a, b] (i.e., two floats a and b)
        * An integer n
        * A number x

    Returns
        * the piecewise linear interpolation value at x,
        * based on n evenly spaced grid points a = x[0] < x[1] < ... < x[n-1] = b

    Here is an example with n = 3:
    http://johnstachurski.net/lectures/images/lininterp.png
"""
def lerp(f, a, b, n, x):
    """
    http://en.wikipedia.org/wiki/Linear_interpolation:

    shows this equation:
        y0 + (x - x0) * ((y1 - y0) / (x1 - x0))
    """
    x0 = a
    x1 = b
    y0 = f(a)
    y1 = f(b)
    return y0 + (x - x0) * ((y1 - y0) / (x1 - x0))

def approx_lerp(f, a, b, n, x):
    """http://en.wikipedia.org/wiki/Linear_interpolation"""
    return f(a) + (((f(b) - f(a)) / (b - a)) * (x - a))

## Filename: linapprox.py
## Author: John Stachurski
def js_linapprox(f, a, b, n, x):
    """
    Evaluates piecewise linear interpolant of f at x on the interval [a, b],
    with n evenly spaced grid points.
    Parameters:
        f is a function
        x, a and b are numbers with a <= x <= b
        n is an integer.
    Returns:
        A number (the interpolant evaluated at x)
    """
    length_of_interval = b - a
    num_subintervals = n - 1
    step = length_of_interval / num_subintervals  # Distance between grid points

    # Find the first grid point that is larger than x
    point = a
    while point <= x:
        point += step
    # Now x must lie between the gridpoints (point - step) and point
    u, v = point - step, point

    return f(u) + (x - u) * (f(v) - f(u)) / (v - u)


import random, math

"""
    Linear interpolation is often used to fill the gaps in a table. Suppose
    that one has a table listing the population of some country in 1970, 1980,
    1990 and 2000, and that one wanted to estimate the population in 1994.
    Linear interpolation is an easy way to do this.

    f(y) = 1000 + (((y - 1970) * 1000) * (.05)
"""
f = lambda y: 1000 + (1000 * (y - 1970) ** 1.8)
a, b = 1970, 2000
n = 4
x = random.randint(a+1, b-1)
js_result = js_linapprox(f, a, b, n, x)

print (a, f(a)), (x, f(x)), (b, f(b))
print '%s -> %s' % ((f, a, b, n, x), js_result)
print lerp(f, a, b, n, x), approx_lerp(f, a, b, n, x), js_linapprox(f, a, b, n, x)



print '%s: ok' % (__file__)

